static GQuark quark_event_mask = 0;
static GQuark quark_extension_event_mode = 0;
static GQuark quark_parent_window = 0;
+static GQuark quark_pointer_window = 0;
static GQuark quark_shape_info = 0;
static GQuark quark_input_shape_info = 0;
static GQuark quark_colormap = 0;
quark_event_mask = g_quark_from_static_string ("gtk-event-mask");
quark_extension_event_mode = g_quark_from_static_string ("gtk-extension-event-mode");
quark_parent_window = g_quark_from_static_string ("gtk-parent-window");
+ quark_pointer_window = g_quark_from_static_string ("gtk-pointer-window");
quark_shape_info = g_quark_from_static_string ("gtk-shape-info");
quark_input_shape_info = g_quark_from_static_string ("gtk-input-shape-info");
quark_colormap = g_quark_from_static_string ("gtk-colormap");
return NULL;
}
+/**
+ * _gtk_widget_set_pointer_window:
+ * @widget: a #GtkWidget.
+ * @pointer_window: the new pointer window.
+ *
+ * Sets pointer window for @widget. Does not ref @pointer_window.
+ * Actually stores it on the #GdkScreen, but you don't need to know that.
+ **/
+void
+_gtk_widget_set_pointer_window (GtkWidget *widget,
+ GdkWindow *pointer_window)
+{
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (widget->window));
+ g_object_set_qdata (G_OBJECT (screen), quark_pointer_window, pointer_window);
+}
+
+/**
+ * _gtk_widget_get_pointer_window:
+ * @widget: a #GtkWidget.
+ *
+ * Return value: the pointer window set on the #GdkScreen @widget is attached
+ * to, or %NULL.
+ **/
+GdkWindow *
+_gtk_widget_get_pointer_window (GtkWidget *widget)
+{
+ g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
+
+ GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (widget->window));
+ return g_object_get_qdata (G_OBJECT (screen), quark_pointer_window);
+}
+
+static void
+synth_crossing (GtkWidget *widget,
+ GdkEventType type,
+ GdkWindow *window,
+ GdkCrossingMode mode,
+ GdkNotifyType detail)
+{
+ GdkEvent *event;
+
+ event = gdk_event_new (type);
+
+ event->crossing.window = g_object_ref (window);
+ event->crossing.send_event = TRUE;
+ event->crossing.subwindow = g_object_ref (window);
+ event->crossing.time = GDK_CURRENT_TIME;
+ event->crossing.x = event->crossing.y = 0;
+ event->crossing.x_root = event->crossing.y_root = 0;
+ event->crossing.mode = mode;
+ event->crossing.detail = detail;
+ event->crossing.focus = FALSE;
+ event->crossing.state = 0;
+
+ if (!widget)
+ widget = gtk_get_event_widget (event);
+
+ if (widget)
+ gtk_widget_event_internal (widget, event);
+
+ gdk_event_free (event);
+}
+
+/**
+ * _gtk_widget_is_pointer_widget:
+ * @widget: a #GtkWidget
+ *
+ * Returns %TRUE if the pointer window belongs to @widget.
+ *
+ */
+gboolean
+_gtk_widget_is_pointer_widget (GtkWidget *widget)
+{
+ if (GTK_WIDGET_HAS_POINTER (widget))
+ {
+ GdkWindow *win;
+ GtkWidget *wid;
+
+ win = _gtk_widget_get_pointer_window (widget);
+ if (win)
+ {
+ gdk_window_get_user_data (win, &wid);
+ if (wid == widget)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ * _gtk_widget_synthesize_crossing:
+ * @from: the #GtkWidget the virtual pointer is leaving.
+ * @to: the #GtkWidget the virtual pointer is moving to.
+ * @mode: the #GdkCrossingMode to place on the synthesized events.
+ *
+ * Generate crossing event(s) on widget state (sensitivity) or GTK+ grab change.
+ *
+ * The real pointer window is the window that most recently received an enter notify
+ * event. Windows that don't select for crossing events can't become the real
+ * poiner window. The real pointer widget that owns the real pointer window. The
+ * effective pointer window is the same as the real pointer window unless the real
+ * pointer widget is either insensitive or there is a grab on a widget that is not
+ * an ancestor of the real pointer widget (in which case the effective pointer
+ * window should be the root window).
+ *
+ * When the effective pointer window is the same as the real poiner window, we
+ * receive crossing events from the windowing system. When the effective pointer
+ * window changes to become different from the real pointer window we synthesize
+ * crossing events, attempting to follow X protocol rules:
+ *
+ * When the root window becomes the effective pointer window:
+ * - leave notify on real pointer window, detail Ancestor
+ * - leave notify on all of its ancestors, detail Virtual
+ * - enter notify on root window, detail Inferior
+ *
+ * When the root window ceases to be the effective pointer window:
+ * - leave notify on root window, detail Inferior
+ * - enter notify on all ancestors of real pointer window, detail Virtual
+ * - enter notify on real pointer window, detail Ancestor
+ */
+void
+_gtk_widget_synthesize_crossing (GtkWidget *from,
+ GtkWidget *to,
+ GdkCrossingMode mode)
+{
+ GdkWindow *from_window = NULL, *to_window = NULL;
+
+ g_return_if_fail (from != NULL || to != NULL);
+
+ if (from != NULL)
+ from_window = GTK_WIDGET_HAS_POINTER (from)
+ ? _gtk_widget_get_pointer_window (from) : from->window;
+ if (to != NULL)
+ to_window = GTK_WIDGET_HAS_POINTER (to)
+ ? _gtk_widget_get_pointer_window (to) : to->window;
+
+ if (from_window == NULL && to_window == NULL)
+ ;
+ else if (from_window != NULL && to_window == NULL)
+ {
+ GList *from_ancestors = NULL, *list;
+ GdkWindow *from_ancestor = from_window;
+
+ while (from_ancestor != NULL)
+ {
+ if (from_ancestor != NULL)
+ {
+ from_ancestor = gdk_window_get_parent (from_ancestor);
+ if (from_ancestor == NULL)
+ break;
+ from_ancestors = g_list_prepend (from_ancestors, from_ancestor);
+ }
+ }
+
+ synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
+ mode, GDK_NOTIFY_ANCESTOR);
+ for (list = g_list_last (from_ancestors); list; list = list->prev)
+ {
+ synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
+ mode, GDK_NOTIFY_VIRTUAL);
+ }
+
+ /* XXX: enter/inferior on root window? */
+
+ g_list_free (from_ancestors);
+ }
+ else if (from_window == NULL && to_window != NULL)
+ {
+ GList *to_ancestors = NULL, *list;
+ GdkWindow *to_ancestor = to_window;
+
+ while (to_ancestor != NULL)
+ {
+ if (to_ancestor != NULL)
+ {
+ to_ancestor = gdk_window_get_parent (to_ancestor);
+ if (to_ancestor == NULL)
+ break;
+ to_ancestors = g_list_prepend (to_ancestors, to_ancestor);
+ }
+ }
+
+ /* XXX: leave/inferior on root window? */
+
+ for (list = to_ancestors; list; list = list->next)
+ {
+ synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
+ mode, GDK_NOTIFY_VIRTUAL);
+ }
+ synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
+ mode, GDK_NOTIFY_ANCESTOR);
+
+ g_list_free (to_ancestors);
+ }
+ else if (from_window == to_window)
+ ;
+ else
+ {
+ GList *from_ancestors = NULL, *to_ancestors = NULL, *list;
+ GdkWindow *from_ancestor = from_window, *to_ancestor = to_window;
+
+ while (from_ancestor != NULL || to_ancestor != NULL)
+ {
+ if (from_ancestor != NULL)
+ {
+ from_ancestor = gdk_window_get_parent (from_ancestor);
+ if (from_ancestor == to_window)
+ break;
+ from_ancestors = g_list_prepend (from_ancestors, from_ancestor);
+ }
+ if (to_ancestor != NULL)
+ {
+ to_ancestor = gdk_window_get_parent (to_ancestor);
+ if (to_ancestor == from_window)
+ break;
+ to_ancestors = g_list_prepend (to_ancestors, to_ancestor);
+ }
+ }
+ if (to_ancestor == from_window)
+ {
+ if (mode != GDK_CROSSING_GTK_UNGRAB)
+ synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
+ mode, GDK_NOTIFY_INFERIOR);
+ for (list = to_ancestors; list; list = list->next)
+ synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
+ mode, GDK_NOTIFY_VIRTUAL);
+ synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
+ mode, GDK_NOTIFY_ANCESTOR);
+ }
+ else if (from_ancestor == to_window)
+ {
+ synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
+ mode, GDK_NOTIFY_ANCESTOR);
+ for (list = g_list_last (from_ancestors); list; list = list->prev)
+ {
+ synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
+ mode, GDK_NOTIFY_VIRTUAL);
+ }
+ if (mode != GDK_CROSSING_GTK_GRAB)
+ synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
+ mode, GDK_NOTIFY_INFERIOR);
+ }
+ else
+ {
+ while (from_ancestors != NULL && to_ancestors != NULL
+ && from_ancestors->data == to_ancestors->data)
+ {
+ from_ancestors = g_list_delete_link (from_ancestors,
+ from_ancestors);
+ to_ancestors = g_list_delete_link (to_ancestors, to_ancestors);
+ }
+
+ synth_crossing (from, GDK_LEAVE_NOTIFY, from_window,
+ mode, GDK_NOTIFY_NONLINEAR);
+
+ for (list = g_list_last (from_ancestors); list; list = list->prev)
+ {
+ synth_crossing (NULL, GDK_LEAVE_NOTIFY, (GdkWindow *) list->data,
+ mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
+ }
+ for (list = to_ancestors; list; list = list->next)
+ {
+ synth_crossing (NULL, GDK_ENTER_NOTIFY, (GdkWindow *) list->data,
+ mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
+ }
+ synth_crossing (to, GDK_ENTER_NOTIFY, to_window,
+ mode, GDK_NOTIFY_NONLINEAR);
+ }
+ g_list_free (from_ancestors);
+ g_list_free (to_ancestors);
+ }
+}
+
static void
gtk_widget_propagate_state (GtkWidget *widget,
GtkStateData *data)
g_signal_emit (widget, widget_signals[STATE_CHANGED], 0, old_state);
+ if (GTK_WIDGET_HAS_POINTER (widget) && !GTK_WIDGET_SHADOWED (widget))
+ {
+ if (!GTK_WIDGET_IS_SENSITIVE (widget))
+ _gtk_widget_synthesize_crossing (widget, NULL,
+ GDK_CROSSING_STATE_CHANGED);
+ else if (old_state == GTK_STATE_INSENSITIVE)
+ _gtk_widget_synthesize_crossing (NULL, widget,
+ GDK_CROSSING_STATE_CHANGED);
+ }
+
if (GTK_IS_CONTAINER (widget))
{
data->parent_sensitive = (GTK_WIDGET_IS_SENSITIVE (widget) != FALSE);
--- /dev/null
+/*
+ * crossingevents.c: A test for crossing events
+ *
+ * Copyright (C) 2008 Cody Russell
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+typedef struct {
+ GtkWidget *window;
+ GtkWidget *eventbox;
+ GtkWidget *frame;
+ GtkWidget *button;
+ GtkWidget *check;
+ gboolean events_connected;
+ GQueue *queue;
+} CrossingTest;
+
+typedef struct {
+ gboolean entered;
+ gchar *name;
+ gboolean synthesized;
+ GdkCrossingMode mode;
+ GdkNotifyType detail;
+} CrossingEventData;
+
+#define SLEEP_DURATION 100
+
+void start_events (CrossingTest *test);
+void stop_events (CrossingTest *test);
+
+static gboolean
+sleep_timeout_cb (gpointer data)
+{
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static void
+sleep_in_main_loop (double fraction)
+{
+ /* process all pending idles and events */
+ while (g_main_context_pending (NULL))
+ g_main_context_iteration (NULL, FALSE);
+ /* sleeping probably isn't strictly necessary here */
+ gdk_threads_add_timeout_full (G_MAXINT, fraction * SLEEP_DURATION, sleep_timeout_cb, NULL, NULL);
+ gtk_main ();
+ /* process any pending idles or events that arrived during sleep */
+ while (g_main_context_pending (NULL))
+ g_main_context_iteration (NULL, FALSE);
+}
+
+void
+set_cursor (GtkWidget *widget)
+{
+ int x, y, w, h;
+
+ gdk_window_get_origin (widget->window, &x, &y);
+
+ x += widget->allocation.x;
+ y += widget->allocation.y;
+ w = widget->allocation.width;
+ h = widget->allocation.height;
+
+ gdk_display_warp_pointer (gtk_widget_get_display (widget),
+ gtk_widget_get_screen (widget),
+ x + w / 2,
+ y + h / 2);
+
+ sleep_in_main_loop (0.5);
+}
+
+static gboolean
+on_enter (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
+{
+ CrossingTest *test = (CrossingTest*)user_data;
+
+ CrossingEventData *evt = g_slice_new0 (CrossingEventData);
+ evt->entered = TRUE;
+ evt->name = g_strdup (gtk_widget_get_name (widget));
+ evt->synthesized = event->send_event;
+ evt->mode = event->mode;
+ evt->detail = event->detail;
+
+ if (!test->queue)
+ test->queue = g_queue_new ();
+
+ g_queue_push_tail (test->queue, evt);
+
+ return FALSE;
+}
+
+static gboolean
+on_leave (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
+{
+ CrossingTest *test = (CrossingTest*)user_data;
+
+ CrossingEventData *evt = g_slice_new0 (CrossingEventData);
+ evt->entered = FALSE;
+ evt->name = g_strdup (gtk_widget_get_name (widget));
+ evt->synthesized = event->send_event;
+ evt->mode = event->mode;
+ evt->detail = event->detail;
+
+ if (!test->queue)
+ test->queue = g_queue_new ();
+
+ g_queue_push_tail (test->queue, evt);
+
+ return FALSE;
+}
+
+static void
+on_check_toggled (GtkWidget *toggle, GtkWidget *button)
+{
+ gtk_widget_set_sensitive (button, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)));
+}
+
+static void
+sensitivity_setup (CrossingTest *test,
+ gconstpointer user_data)
+{
+ GtkWidget *frame;
+
+ test->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name (test->window, "W");
+ frame = gtk_frame_new ("Crossing Events");
+ test->eventbox = gtk_event_box_new ();
+ gtk_widget_set_name (test->eventbox, "E");
+
+ GtkWidget *vbox = gtk_vbox_new (FALSE, 10);
+ gtk_container_add (GTK_CONTAINER (test->window), frame);
+ gtk_container_add (GTK_CONTAINER (frame), test->eventbox);
+ gtk_container_add (GTK_CONTAINER (test->eventbox), vbox);
+
+ test->button = gtk_button_new_with_label ("Click me!");
+ gtk_widget_set_name (test->button, "B");
+ gtk_box_pack_start (GTK_BOX (vbox), test->button, FALSE, TRUE, 0);
+
+ test->check = gtk_check_button_new_with_label ("Sensitive?");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), TRUE);
+ g_signal_connect (G_OBJECT (test->check),
+ "toggled", G_CALLBACK (on_check_toggled), test->button);
+ gtk_widget_set_name (test->check, "C");
+ gtk_box_pack_start (GTK_BOX (vbox), test->check, FALSE, TRUE, 0);
+
+ gtk_widget_show_all (test->window);
+
+ gtk_window_move (GTK_WINDOW (test->window), 0, 0);
+
+ sleep_in_main_loop (0.5);
+}
+
+static void
+sensitivity_teardown (CrossingTest *test,
+ gconstpointer user_data)
+{
+ stop_events (test);
+ gtk_widget_destroy (test->window);
+
+ if (test->queue != NULL)
+ {
+ g_queue_clear (test->queue);
+ test->queue = NULL;
+ }
+}
+
+void
+start_events (CrossingTest *test)
+{
+ if (!test->events_connected)
+ {
+ g_object_connect (G_OBJECT (test->window),
+ "signal::destroy", gtk_main_quit, NULL,
+ "signal::enter-notify-event", on_enter, test,
+ "signal::leave-notify-event", on_leave, test,
+ NULL);
+ g_object_connect (G_OBJECT (test->eventbox),
+ "signal::enter-notify-event", on_enter, test,
+ "signal::leave-notify-event", on_leave, test,
+ NULL);
+ g_object_connect (G_OBJECT (test->button),
+ "signal::enter-notify-event", on_enter, test,
+ "signal::leave-notify-event", on_leave, test,
+ NULL);
+ g_object_connect (G_OBJECT (test->check),
+ "signal::enter-notify-event", on_enter, test,
+ "signal::leave-notify-event", on_leave, test,
+ NULL);
+ test->events_connected = TRUE;
+ }
+
+ sleep_in_main_loop (0.5);
+}
+
+void
+stop_events (CrossingTest *test)
+{
+ if (test->events_connected)
+ {
+ g_object_disconnect (G_OBJECT (test->window),
+ "any_signal", gtk_main_quit, NULL,
+ "any_signal", on_enter, test,
+ "any_signal", on_leave, test,
+ NULL);
+ g_object_disconnect (G_OBJECT (test->eventbox),
+ "any_signal", on_enter, test,
+ "any_signal", on_leave, test,
+ NULL);
+ g_object_disconnect (G_OBJECT (test->button),
+ "any_signal", on_enter, test,
+ "any_signal", on_leave, test,
+ NULL);
+ g_object_disconnect (G_OBJECT (test->check),
+ "any_signal", G_CALLBACK (on_check_toggled), test->button,
+ "any_signal", on_enter, test,
+ "any_signal", on_leave, test,
+ NULL);
+ test->events_connected = FALSE;
+ }
+}
+
+void
+move_cursor_away (CrossingTest *test)
+{
+ gdk_display_warp_pointer (gtk_widget_get_display (test->window),
+ gtk_widget_get_screen (test->window),
+ 1000, -1000);
+
+ sleep_in_main_loop (0.5);
+}
+
+void
+check_event (CrossingTest *test,
+ const gchar *name,
+ gboolean entered,
+ gboolean synthesized,
+ GdkCrossingMode mode,
+ GdkNotifyType detail)
+{
+ CrossingEventData *evt;
+
+ g_assert (test->queue != NULL);
+
+ evt = g_queue_pop_head (test->queue);
+
+ g_assert (evt->entered == entered);
+ g_assert (strcmp (evt->name, name) == 0);
+ g_assert (evt->synthesized == synthesized);
+ g_assert (evt->mode == mode);
+
+ if (evt->detail != detail)
+ g_print ("detail, evt %d vs %d\n", evt->detail, detail);
+
+ g_assert (evt->detail == detail);
+}
+
+/* Verify crossing events when moving into and out of a sensitive widget */
+static void
+cursor_on_sensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ move_cursor_away (test);
+
+ start_events (test);
+
+ set_cursor (test->button);
+
+ check_event (test,
+ "W",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR_VIRTUAL);
+
+ check_event (test,
+ "E",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR_VIRTUAL);
+
+ check_event (test,
+ "B",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ move_cursor_away (test);
+
+ check_event (test,
+ "B",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ check_event (test,
+ "E",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR_VIRTUAL);
+
+ check_event (test,
+ "W",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR_VIRTUAL);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+change_sensitive_to_insensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ move_cursor_away (test);
+ set_cursor (test->button);
+
+ start_events (test);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
+
+ check_event (test,
+ "B",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "E",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_VIRTUAL);
+
+ check_event (test,
+ "W",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_VIRTUAL);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+change_insensitive_to_sensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ move_cursor_away (test);
+ set_cursor (test->button);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
+
+ start_events (test);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), TRUE);
+
+ check_event (test,
+ "W",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_VIRTUAL);
+
+ check_event (test,
+ "E",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_VIRTUAL);
+
+ check_event (test,
+ "B",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_ANCESTOR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+cursor_from_insensitive_to_sensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->button);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
+
+ start_events (test);
+
+ set_cursor (test->check);
+
+ check_event (test,
+ "C",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+cursor_from_sensitive_to_insensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->check);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
+
+ start_events (test);
+
+ set_cursor (test->button);
+
+ check_event (test,
+ "C",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+add_gtk_grab (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->button);
+
+ start_events (test);
+
+ gtk_grab_add (test->check);
+
+ check_event (test,
+ "B",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_GRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "E",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_GRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "W",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_GRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+remove_gtk_grab (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->button);
+
+ gtk_grab_add (test->check);
+
+ start_events (test);
+
+ gtk_grab_remove (test->check);
+
+ check_event (test,
+ "B",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_UNGRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "E",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_UNGRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "W",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_UNGRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+cursor_from_shadowed_to_unshadowed (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->button);
+
+ gtk_grab_add (test->check);
+
+ start_events (test);
+
+ set_cursor (test->check);
+
+ check_event (test,
+ "C",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ check_event (test,
+ "C",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+cursor_from_unshadowed_to_shadowed (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->check);
+
+ gtk_grab_add (test->check);
+
+ start_events (test);
+
+ set_cursor (test->button);
+
+ check_event (test,
+ "C",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ check_event (test,
+ "C",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ gtk_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/crossings/cursor-on-sensitive", CrossingTest, NULL,
+ sensitivity_setup, cursor_on_sensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/change-sensitive-to-insensitive", CrossingTest, NULL,
+ sensitivity_setup, change_sensitive_to_insensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/cursor-from-insensitive-to-sensitive", CrossingTest, NULL,
+ sensitivity_setup, cursor_from_insensitive_to_sensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/cursor-from-sensitive-to-insensitive", CrossingTest, NULL,
+ sensitivity_setup, cursor_from_sensitive_to_insensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/change-insensitive-to-sensitive", CrossingTest, NULL,
+ sensitivity_setup, change_insensitive_to_sensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/add-gtk-grab", CrossingTest, NULL,
+ sensitivity_setup, add_gtk_grab, sensitivity_teardown);
+
+ g_test_add ("/crossings/remove-gtk-grab", CrossingTest, NULL,
+ sensitivity_setup, remove_gtk_grab, sensitivity_teardown);
+
+ g_test_add ("/crossings/cursor-from-shadowed-to-unshadowed", CrossingTest, NULL,
+ sensitivity_setup, cursor_from_shadowed_to_unshadowed, sensitivity_teardown);
+
+ g_test_add ("/crossings/cursor-from-unshadowed-to-shadowed", CrossingTest, NULL,
+ sensitivity_setup, cursor_from_unshadowed_to_shadowed, sensitivity_teardown);
+
+ return g_test_run ();
+}
+/*
+ * crossingevents.c: A test for crossing events
+ *
+ * Copyright (C) 2008 Cody Russell
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <string.h>
+
+typedef struct {
+ GtkWidget *window;
+ GtkWidget *eventbox;
+ GtkWidget *frame;
+ GtkWidget *button;
+ GtkWidget *check;
+ gboolean events_connected;
+ GQueue *queue;
+} CrossingTest;
+
+typedef struct {
+ gboolean entered;
+ gchar *name;
+ gboolean synthesized;
+ GdkCrossingMode mode;
+ GdkNotifyType detail;
+} CrossingEventData;
+
+#define SLEEP_DURATION 100
+
+void start_events (CrossingTest *test);
+void stop_events (CrossingTest *test);
+
+static gboolean
+sleep_timeout_cb (gpointer data)
+{
+ gtk_main_quit ();
+ return FALSE;
+}
+
+static void
+sleep_in_main_loop (double fraction)
+{
+ /* process all pending idles and events */
+ while (g_main_context_pending (NULL))
+ g_main_context_iteration (NULL, FALSE);
+ /* sleeping probably isn't strictly necessary here */
+ gdk_threads_add_timeout_full (G_MAXINT, fraction * SLEEP_DURATION, sleep_timeout_cb, NULL, NULL);
+ gtk_main ();
+ /* process any pending idles or events that arrived during sleep */
+ while (g_main_context_pending (NULL))
+ g_main_context_iteration (NULL, FALSE);
+}
+
+void
+set_cursor (GtkWidget *widget)
+{
+ int x, y, w, h;
+
+ gdk_window_get_origin (widget->window, &x, &y);
+
+ x += widget->allocation.x;
+ y += widget->allocation.y;
+ w = widget->allocation.width;
+ h = widget->allocation.height;
+
+ gdk_display_warp_pointer (gtk_widget_get_display (widget),
+ gtk_widget_get_screen (widget),
+ x + w / 2,
+ y + h / 2);
+
+ sleep_in_main_loop (0.5);
+}
+
+static gboolean
+on_enter (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
+{
+ CrossingTest *test = (CrossingTest*)user_data;
+
+ CrossingEventData *evt = g_slice_new0 (CrossingEventData);
+ evt->entered = TRUE;
+ evt->name = g_strdup (gtk_widget_get_name (widget));
+ evt->synthesized = event->send_event;
+ evt->mode = event->mode;
+ evt->detail = event->detail;
+
+ if (!test->queue)
+ test->queue = g_queue_new ();
+
+ g_queue_push_tail (test->queue, evt);
+
+ return FALSE;
+}
+
+static gboolean
+on_leave (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data)
+{
+ CrossingTest *test = (CrossingTest*)user_data;
+
+ CrossingEventData *evt = g_slice_new0 (CrossingEventData);
+ evt->entered = FALSE;
+ evt->name = g_strdup (gtk_widget_get_name (widget));
+ evt->synthesized = event->send_event;
+ evt->mode = event->mode;
+ evt->detail = event->detail;
+
+ if (!test->queue)
+ test->queue = g_queue_new ();
+
+ g_queue_push_tail (test->queue, evt);
+
+ return FALSE;
+}
+
+static void
+on_check_toggled (GtkWidget *toggle, GtkWidget *button)
+{
+ gtk_widget_set_sensitive (button, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (toggle)));
+}
+
+static void
+sensitivity_setup (CrossingTest *test,
+ gconstpointer user_data)
+{
+ GtkWidget *frame;
+
+ test->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_name (test->window, "W");
+ frame = gtk_frame_new ("Crossing Events");
+ test->eventbox = gtk_event_box_new ();
+ gtk_widget_set_name (test->eventbox, "E");
+
+ GtkWidget *vbox = gtk_vbox_new (FALSE, 10);
+ gtk_container_add (GTK_CONTAINER (test->window), frame);
+ gtk_container_add (GTK_CONTAINER (frame), test->eventbox);
+ gtk_container_add (GTK_CONTAINER (test->eventbox), vbox);
+
+ test->button = gtk_button_new_with_label ("Click me!");
+ gtk_widget_set_name (test->button, "B");
+ gtk_box_pack_start (GTK_BOX (vbox), test->button, FALSE, TRUE, 0);
+
+ test->check = gtk_check_button_new_with_label ("Sensitive?");
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), TRUE);
+ g_signal_connect (G_OBJECT (test->check),
+ "toggled", G_CALLBACK (on_check_toggled), test->button);
+ gtk_widget_set_name (test->check, "C");
+ gtk_box_pack_start (GTK_BOX (vbox), test->check, FALSE, TRUE, 0);
+
+ gtk_widget_show_all (test->window);
+
+ gtk_window_move (GTK_WINDOW (test->window), 0, 0);
+
+ sleep_in_main_loop (0.5);
+}
+
+static void
+sensitivity_teardown (CrossingTest *test,
+ gconstpointer user_data)
+{
+ stop_events (test);
+ gtk_widget_destroy (test->window);
+
+ if (test->queue != NULL)
+ {
+ g_queue_clear (test->queue);
+ test->queue = NULL;
+ }
+}
+
+void
+start_events (CrossingTest *test)
+{
+ if (!test->events_connected)
+ {
+ g_object_connect (G_OBJECT (test->window),
+ "signal::destroy", gtk_main_quit, NULL,
+ "signal::enter-notify-event", on_enter, test,
+ "signal::leave-notify-event", on_leave, test,
+ NULL);
+ g_object_connect (G_OBJECT (test->eventbox),
+ "signal::enter-notify-event", on_enter, test,
+ "signal::leave-notify-event", on_leave, test,
+ NULL);
+ g_object_connect (G_OBJECT (test->button),
+ "signal::enter-notify-event", on_enter, test,
+ "signal::leave-notify-event", on_leave, test,
+ NULL);
+ g_object_connect (G_OBJECT (test->check),
+ "signal::enter-notify-event", on_enter, test,
+ "signal::leave-notify-event", on_leave, test,
+ NULL);
+ test->events_connected = TRUE;
+ }
+
+ sleep_in_main_loop (0.5);
+}
+
+void
+stop_events (CrossingTest *test)
+{
+ if (test->events_connected)
+ {
+ g_object_disconnect (G_OBJECT (test->window),
+ "any_signal", gtk_main_quit, NULL,
+ "any_signal", on_enter, test,
+ "any_signal", on_leave, test,
+ NULL);
+ g_object_disconnect (G_OBJECT (test->eventbox),
+ "any_signal", on_enter, test,
+ "any_signal", on_leave, test,
+ NULL);
+ g_object_disconnect (G_OBJECT (test->button),
+ "any_signal", on_enter, test,
+ "any_signal", on_leave, test,
+ NULL);
+ g_object_disconnect (G_OBJECT (test->check),
+ "any_signal", G_CALLBACK (on_check_toggled), test->button,
+ "any_signal", on_enter, test,
+ "any_signal", on_leave, test,
+ NULL);
+ test->events_connected = FALSE;
+ }
+}
+
+void
+move_cursor_away (CrossingTest *test)
+{
+ gdk_display_warp_pointer (gtk_widget_get_display (test->window),
+ gtk_widget_get_screen (test->window),
+ 1000, -1000);
+
+ sleep_in_main_loop (0.5);
+}
+
+void
+check_event (CrossingTest *test,
+ const gchar *name,
+ gboolean entered,
+ gboolean synthesized,
+ GdkCrossingMode mode,
+ GdkNotifyType detail)
+{
+ CrossingEventData *evt;
+
+ g_assert (test->queue != NULL);
+
+ evt = g_queue_pop_head (test->queue);
+
+ g_assert (evt->entered == entered);
+ g_assert (strcmp (evt->name, name) == 0);
+ g_assert (evt->synthesized == synthesized);
+ g_assert (evt->mode == mode);
+
+ if (evt->detail != detail)
+ g_print ("detail, evt %d vs %d\n", evt->detail, detail);
+
+ g_assert (evt->detail == detail);
+}
+
+/* Verify crossing events when moving into and out of a sensitive widget */
+static void
+cursor_on_sensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ move_cursor_away (test);
+
+ start_events (test);
+
+ set_cursor (test->button);
+
+ check_event (test,
+ "W",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR_VIRTUAL);
+
+ check_event (test,
+ "E",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR_VIRTUAL);
+
+ check_event (test,
+ "B",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ move_cursor_away (test);
+
+ check_event (test,
+ "B",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ check_event (test,
+ "E",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR_VIRTUAL);
+
+ check_event (test,
+ "W",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR_VIRTUAL);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+change_sensitive_to_insensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ move_cursor_away (test);
+ set_cursor (test->button);
+
+ start_events (test);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
+
+ check_event (test,
+ "B",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "E",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_VIRTUAL);
+
+ check_event (test,
+ "W",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_VIRTUAL);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+change_insensitive_to_sensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ move_cursor_away (test);
+ set_cursor (test->button);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
+
+ start_events (test);
+
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), TRUE);
+
+ check_event (test,
+ "W",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_VIRTUAL);
+
+ check_event (test,
+ "E",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_VIRTUAL);
+
+ check_event (test,
+ "B",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_STATE_CHANGED,
+ GDK_NOTIFY_ANCESTOR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+cursor_from_insensitive_to_sensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->button);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
+
+ start_events (test);
+
+ set_cursor (test->check);
+
+ check_event (test,
+ "C",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+cursor_from_sensitive_to_insensitive (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->check);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (test->check), FALSE);
+
+ start_events (test);
+
+ set_cursor (test->button);
+
+ check_event (test,
+ "C",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+add_gtk_grab (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->button);
+
+ start_events (test);
+
+ gtk_grab_add (test->check);
+
+ check_event (test,
+ "B",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_GRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "E",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_GRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "W",
+ FALSE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_GRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+remove_gtk_grab (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->button);
+
+ gtk_grab_add (test->check);
+
+ start_events (test);
+
+ gtk_grab_remove (test->check);
+
+ check_event (test,
+ "B",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_UNGRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "E",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_UNGRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ check_event (test,
+ "W",
+ TRUE,
+ TRUE, /* synthesized */
+ GDK_CROSSING_GTK_UNGRAB,
+ GDK_NOTIFY_ANCESTOR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+cursor_from_shadowed_to_unshadowed (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->button);
+
+ gtk_grab_add (test->check);
+
+ start_events (test);
+
+ set_cursor (test->check);
+
+ check_event (test,
+ "C",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ check_event (test,
+ "C",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+static void
+cursor_from_unshadowed_to_shadowed (CrossingTest *test,
+ gconstpointer user_data)
+{
+ set_cursor (test->check);
+
+ gtk_grab_add (test->check);
+
+ start_events (test);
+
+ set_cursor (test->button);
+
+ check_event (test,
+ "C",
+ FALSE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ check_event (test,
+ "C",
+ TRUE,
+ FALSE, /* native */
+ GDK_CROSSING_NORMAL,
+ GDK_NOTIFY_NONLINEAR);
+
+ g_assert (g_queue_is_empty (test->queue));
+
+ stop_events (test);
+}
+
+int
+main (int argc,
+ char **argv)
+{
+ gtk_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/crossings/cursor-on-sensitive", CrossingTest, NULL,
+ sensitivity_setup, cursor_on_sensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/change-sensitive-to-insensitive", CrossingTest, NULL,
+ sensitivity_setup, change_sensitive_to_insensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/cursor-from-insensitive-to-sensitive", CrossingTest, NULL,
+ sensitivity_setup, cursor_from_insensitive_to_sensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/cursor-from-sensitive-to-insensitive", CrossingTest, NULL,
+ sensitivity_setup, cursor_from_sensitive_to_insensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/change-insensitive-to-sensitive", CrossingTest, NULL,
+ sensitivity_setup, change_insensitive_to_sensitive, sensitivity_teardown);
+
+ g_test_add ("/crossings/add-gtk-grab", CrossingTest, NULL,
+ sensitivity_setup, add_gtk_grab, sensitivity_teardown);
+
+ g_test_add ("/crossings/remove-gtk-grab", CrossingTest, NULL,
+ sensitivity_setup, remove_gtk_grab, sensitivity_teardown);
+
+ g_test_add ("/crossings/cursor-from-shadowed-to-unshadowed", CrossingTest, NULL,
+ sensitivity_setup, cursor_from_shadowed_to_unshadowed, sensitivity_teardown);
+
+ g_test_add ("/crossings/cursor-from-unshadowed-to-shadowed", CrossingTest, NULL,
+ sensitivity_setup, cursor_from_unshadowed_to_shadowed, sensitivity_teardown);
+
+ return g_test_run ();
+}